package com.jy.oms.framework.asyn.task.checker;

import com.jy.oms.framework.asyn.task.checker.model.ExecutorCheckParam;
import com.jy.oms.framework.asyn.task.checker.model.ExecutorCheckResult;
import com.jy.oms.framework.asyn.task.cluster.config.ITaskConfigService;
import com.jy.oms.framework.asyn.task.constants.CacheConstants;
import com.jy.oms.framework.asyn.task.constants.TimeUnitEnum;
import com.jy.oms.framework.asyn.task.core.RedissonClientProxy;
import com.jy.oms.framework.asyn.task.handler.ITaskHandlerManager;
import com.jy.oms.framework.asyn.task.handler.ITaskHandlerProcessor;
import com.jy.oms.framework.asyn.task.model.TaskGroup;
import com.jy.oms.framework.asyn.task.model.TaskHandlerWrapper;
import com.jy.oms.framework.asyn.task.model.strategy.LimiterExecuteStrategy;
import org.apache.commons.lang3.time.DateUtils;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 限速执行器校验
 *
 * @author liuquan
 */
@Service
public class RateLimitChecker implements IExecutorChecker, ITaskHandlerProcessor {

    @Autowired
    private ITaskConfigService configService;
    @Autowired
    private ITaskHandlerManager taskHandlerManager;

    @Override
    public void processAfterTaskHandlerInitialization() {
        Map<TaskGroup, List<TaskHandlerWrapper>> taskGroupListMap = taskHandlerManager.loadAllExecutableTaskHandler();
        taskGroupListMap.entrySet().stream()
                .map(Map.Entry::getValue)
                .flatMap(Collection::stream)
                .filter(TaskHandlerWrapper::isAdjustableRateLimiter)
                .forEach(taskHandlerWrapper -> {
                    RateLimiterConfigListener listener = new RateLimiterConfigListener(taskHandlerWrapper.getTaskCode(), this::initTaskRateLimiter);
                    configService.addTaskConfigChangeListener(listener);
                });
    }

    private void initTaskRateLimiter(TaskHandlerWrapper taskHandlerWrapper) {
        RedissonClient redissonClient = RedissonClientProxy.getRedissonClient();
        LimiterExecuteStrategy executeStrategy = (LimiterExecuteStrategy) taskHandlerWrapper.getExecuteStrategy();
        String cacheKey = CacheConstants.genCacheKey(CacheConstants.REQUEST_RATE_LIMITER, taskHandlerWrapper.getTaskCode());
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(cacheKey);
        if (rateLimiter.isExists()) {
            rateLimiter.delete();
        }
        rateLimiter.trySetRate(RateType.OVERALL, executeStrategy.getLimiter(),
                executeStrategy.getInterval(), TimeUnitEnum.toRateIntervalUnit(executeStrategy.getTimeUnit()));
    }

    @Override
    public ExecutorCheckResult canExecute(ExecutorCheckParam executorCheckParam) {
        String taskCode = executorCheckParam.getExecutorCode();
        TaskHandlerWrapper taskHandlerWrapper = taskHandlerManager.loadTaskHandler(taskCode);
        if (taskHandlerWrapper.isNeedRateLimit()) {
            RedissonClient redissonClient = RedissonClientProxy.getRedissonClient();
            String cacheKey = CacheConstants.genCacheKey(CacheConstants.REQUEST_RATE_LIMITER, taskHandlerWrapper.getTaskCode());
            RRateLimiter rateLimiter = redissonClient.getRateLimiter(cacheKey);
            boolean canRun = rateLimiter.tryAcquire();
            ExecutorCheckResult checkResult = new ExecutorCheckResult(canRun);
            if (!canRun) {
                //当任务限速后，重新设置最早可执行时间
                LimiterExecuteStrategy executeStrategy = (LimiterExecuteStrategy) taskHandlerWrapper.getExecuteStrategy();
                Date executeTime = DateUtils.addSeconds(new Date(), executeStrategy.toSeconds());
                checkResult.setEarliestExecuteTime(executeTime);
            }

            return checkResult;
        }

        return ExecutorCheckResult.TRUE;
    }

}
